/*
 * <h3>Title:wheel-utils系统</h3>
 * <p>Copyright (c) 2019, wnfbx@163.com. All rights reserved.</p>
 * @author wnfbx@163.com
 */
package wang.naifei.core.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wang.naifei.core.utils.io.IOUtil;

import java.io.*;
import java.rmi.server.UID;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <dt>Description:FileUtils</dt>
 * <dd>文件工具类</dd>
 * <dd>author: wnfbx@163.com</dd>
 * <dd>version:1.0 <i>2022/1/14 23:48</i></dd>
 *
 * @since jdk1.7+
 */
public class FileUtils {

    private static final Logger log = LoggerFactory.getLogger(FileUtils.class);

    /**
     * <dt>Description:lfName</dt>
     * <dd>legal File Name 合法文件名，注长度未处理</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/16 21:00</i></dd>
     *
     * @param fileName
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String lfName(String fileName) {
        //Windows API对文件路径的限制为260个字符，文件名最大不超过257(去掉盘符)
        fileName = fileName.length() > 257 ? fileName.substring(0, 257) : fileName;
        //替换不合规字符
        Pattern pattern = Pattern.compile("[\\\\/:\\*\\?\\\"<>\\|]");
        Matcher matcher = pattern.matcher(fileName);
        return matcher.replaceAll(" "); // 将匹配到的非法字符替换
    }

    /**
     * <dt>Description:newFile</dt>
     * <dd>新建文件<p>
     * 不追加，内容为空不为null即覆盖文件</dd>
     * <dd>author: wnfbx</dd>
     * <dd>version:1.0 <i>2022/1/15 12:51</i></dd>
     *
     * @param file          目标文件
     * @param fileContent   文件内容
     * @param contentAppend 追加内容
     * @return java.io.File
     * @since jdk1.7+
     */
    public static File newFile(File file, String fileContent, boolean contentAppend) {
        try {
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            writeFileContent(file, fileContent, contentAppend);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    public static File newFile(String filePath, String fileContent, boolean contentAppend) {
        return newFile(new File(filePath), fileContent, contentAppend);
    }

    /**
     * <dt>Description:writeFileContent</dt>
     * <dd></dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/2/12 1:49</i></dd>
     *
     * @param file          写入文件
     * @param fileContent   写入内容
     * @param contentAppend 是否追加
     * @since jdk1.7+
     */
    public static void writeFileContent(File file, String fileContent, boolean contentAppend) throws IOException {
        if (file.canWrite() && fileContent != null) {
            FileWriter fileWriter = new FileWriter(file, contentAppend);
            fileWriter.write(fileContent);
            fileWriter.close();
        }
    }

    /**
     * <dt>Description:delFile</dt>
     * <dd>只删除文件</dd>
     * <dd>author: wnfbx</dd>
     * <dd>version:1.0 <i>2022/1/15 12:52</i></dd>
     *
     * @param file 删除文件
     * @return boolean
     * @since jdk1.7+
     */
    public static boolean delFile(File file) {
        boolean r = false;
        if (file != null) {
            if (file.exists()) {
                if (file.isFile()) {
                    r = file.delete();
                } else {
                    log.debug("参数file不是文件");
                }
            } else {
                log.debug("文件不存在");
            }
        } else {
            log.debug("参数 file 为 null");
        }
        return r;
    }

    /**
     * <dt>Description:newFolder</dt>
     * <dd>新建目录</dd>
     * <dd>author: wnfbx</dd>
     * <dd>version:1.0 <i>2022/1/15 12:56</i></dd>
     *
     * @param path 目录路径
     * @return boolean
     * @since jdk1.7+
     */
    public static boolean newFolder(String path) {
        return newFolder(new File(path));
    }

    public static boolean newFolder(File f) {
        if (f.isDirectory()) {
            return f.mkdirs();
        } else {
            return f.getParentFile().mkdirs();
        }
    }

//    /**
//     * <dt>Description:deleteFolder</dt>
//     * <dd>删除文件夹</dd>
//     * <dd>author: wnfbx</dd>
//     * <dd>version:1.0 <i>2022/1/15 12:57</i></dd>
//     *
//     * @param directory
//     * @return boolean
//     * @since jdk1.7+
//     */
//    public static boolean delFolder(File directory) {
//        boolean r = false;
//        try {
//            deleteDirectory(directory);
//            r = true;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return r;
//    }

//    /**
//     * 删除文件夹里面的所有文件
//     * <p>
//     *
//     * @param folder 文件夹
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月8日 下午5:20:03
//     * @version 1.0
//     */
//    public static boolean cleanFolder(File directory) {
//        boolean r = false;
//        try {
//            cleanDirectory(directory);
//            r = true;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return r;
//    }

//    /**
//     * 复制单个文件
//     * <p>
//     *
//     * @param srcFile
//     * @param destFile
//     * @param overwrite
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月8日 下午7:30:04
//     * @version 1.0
//     */
//    public static boolean fileCopy(File srcFile, File destFile, boolean overwrite) {
//        boolean r = false;
//        if (!overwrite && destFile.exists()) {
//            Log.INSTANCE.warn("文件已存在");
//        } else {
//            try {
//                copyFile(srcFile, destFile); // 默认可以覆盖目标文件
//                r = true;
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//        return r;
//    }
//
//    public static boolean fileCopy(String srcFile, String destFile, boolean overwrite) {
//        return fileCopy(new File(srcFile), new File(destFile), overwrite);
//    }
//
//    /**
//     * 复制整个文件夹内容
//     * <p>
//     *
//     * @param srcDir
//     * @param destDir
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月8日 下午7:42:42
//     * @version 1.0
//     */
//    public static boolean copyFolder(File srcDir, File destDir) {
//        boolean r = false;
//        try {
//            copyDirectory(srcDir, destDir);
//            r = true;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return r;
//    }

//    /**
//     * 移动文件
//     * <p>
//     *
//     * @param srcFile
//     * @param destFile
//     * @param overwrite
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月8日 下午7:45:42
//     * @version 1.0
//     */
//    public static boolean moveFile(File srcFile, File destFile, boolean overwrite) {
//        boolean r = false;
//        if (overwrite && destFile.exists() && destFile.isFile()) {
//            destFile.delete();
//        }
//        try {
//            moveFile(srcFile, destFile);// 目标文件存在会抛异常
//            r = true;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return r;
//    }
//
//    /**
//     * 移动文件
//     * <p>
//     * 如果文件已存在则重命名文件
//     *
//     * @param srcfile
//     * @param destFile
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月23日 下午12:52:48
//     * @version 1.0
//     */
//    public static File moveFileRenameOnExist(File srcfile, File destFile) {
//        // 目标文件存在
//        if (destFile.exists()) {
//            // 重命名文件
//            File newExistFileName = FileUtils.newExistFileName(destFile, null);
//            while (newExistFileName.exists()) {
//                newExistFileName = FileUtils.newExistFileName(destFile, null);
//            }
//            Log.INSTANCE.debug("文件{}已存在,并改名为{}", destFile.getPath(), newExistFileName.getPath());
//            destFile.renameTo(newExistFileName);
//        }
//        boolean moveFile = FileUtils.moveFile(srcfile, destFile, false);
//        if (moveFile) {
//            return destFile;
//        }
//        return null;
//    }
//
//    /**
//     * 移动文件夹
//     * <p>
//     *
//     * @param srcDir
//     * @param destDir
//     * @return
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月8日 下午7:50:00
//     * @version 1.0
//     */
//    public static boolean moveFolder(File srcDir, File destDir) {
//        boolean r = false;
//        try {
//            moveDirectory(srcDir, destDir);
//            r = true;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return r;
//    }

    /**
     * <dt>Description:getAllFiles</dt>
     * <dd>递归遍历所有文件</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 14:53</i></dd>
     *
     * @param root   便利目录
     * @param list   缓存列表
     * @param filter 过滤器
     * @since jdk1.7+
     */
    public static void getAllFiles(File root, List<File> list, FileFilter filter) {
        if (root.isFile() && (filter == null || filter.accept(root))) {
            list.add(root);
        } else if (root.isDirectory()) {
            File[] fs = root.listFiles();
            if (fs != null) {
                for (File f : fs) {
                    getAllFiles(f, list, filter);
                }
            }
        }
    }

    /**
     * <dt>Description:getAllFiles</dt>
     * <dd>递归遍历所有文件</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 14:58</i></dd>
     *
     * @param root   目标目录
     * @param filter 过滤器
     * @return java.util.List<java.io.File>
     * @since jdk1.7+
     */
    public static List<File> getAllFiles(File root, FileFilter filter) {
        List<File> fileList = new ArrayList<>();
        getAllFiles(root, fileList, filter);
        return fileList;
    }

    /**
     * <dt>Description:getAllFiles</dt>
     * <dd>递归遍历所有文件</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 14:58</i></dd>
     *
     * @param root 目标目录
     * @return java.util.List<java.io.File>
     * @since jdk1.7+
     */
    public static List<File> getAllFiles(File root) {
        return getAllFiles(root, null);
    }


    /**
     * <dt>Description:createFileNameByTimeAndRandom</dt>
     * <dd>根据系统时间加随机数生成文件名<p>
     * * 例：yyyyMMddHHmmssSSS_dadw12</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:28</i></dd>
     *
     * @param fileExt         文件后缀名
     * @param randomStringLen 随机字符长度
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String createFileNameByTimeAndRandom(String fileExt, int randomStringLen) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return sdf.format(new Date()) + "_" + StringUtils.getRandomString(randomStringLen) + "." + fileExt;
    }

    /**
     * <dt>Description:createFileNameByDateAndUID</dt>
     * <dd>根据系统日期加 UID 生成上传后保存的文件名
     * <p></dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:29</i></dd>
     *
     * @param fileExt 件后缀名
     * @param uid     uid
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String createFileNameByDateAndUID(String fileExt, String uid) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        if (uid == null) {
            uid = new UID().toString().replaceAll(":", "-");
        }
        StringBuilder append = new StringBuilder().append(sdf.format(new Date())).append("_").append(uid);
        //兼容
        if (!fileExt.startsWith("\\.")) {
            append.append(".");
        }
        return append.append(fileExt).toString();
    }

    /**
     * <dt>Description:fileSizeToString</dt>
     * <dd>文件大小转字符串</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:29</i></dd>
     *
     * @param fileSize 文件大小
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String fileSizeToString(long fileSize) {
        String[] units = {"B", "K", "M", "G", "T"};
        int i = 0, l = units.length - 1;
        long s = fileSize;
        while (s >= 1024 && i < l) {
            s /= 1024;
            i++;
        }
        return String.format("%.2f", s) + units[i];
    }

    /**
     * <dt>Description:readFileMd5</dt>
     * <dd>文件内容md5签名</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:38</i></dd>
     *
     * @param inputStream 前面内容
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String readFileMd5(InputStream inputStream) throws NoSuchAlgorithmException {
        String r;
        // 拿到一个MD5转换器（同样，这里可以换成SHA1）
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[1024 * 64];
        try (DigestInputStream digestInputStream = new DigestInputStream(inputStream, messageDigest)) {
            while (digestInputStream.read(buffer) > 0) {
                // doNothing
            }
            // 获取最终的MessageDigest
            messageDigest = digestInputStream.getMessageDigest();
        } catch (IOException e) {
            log.error("digestInputStream read error", e);
        }
        r = StringUtils.toHex(messageDigest.digest());
        return r;
    }

    /**
     * <dt>Description:getFilenameExtension</dt>
     * <dd>获取文件后缀名</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 17:25</i></dd>
     *
     * @param path 目标文件
     * @return java.lang.String
     * @see 、系统 windows 可以建点开头(.aaa)和含有点(a.aa)的目录 windows 可以建点开头(.aaa)的文件
     * @since jdk1.7+
     */
    public static String getFilenameExtension(String path) {
        File file = new File(path);
        return getFilenameExtension(file);
    }

    /**
     * <dt>Description:getFilenameExtension</dt>
     * <dd>获取文件后缀名
     * <p>
     * 注：windows 可以建点开头(.aaa)和含有点(a.aa)的目录 windows 可以建点开头(.aaa)的文件
     * </p></dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:50</i></dd>
     *
     * @param file 目标文件
     * @return java.lang.String
     * @since jdk1.7+
     */
    public static String getFilenameExtension(File file) {
        if (file.exists() && file.isDirectory()) {// 判断如果是目录则不返回后缀名
            return null;
        }
        return org.springframework.util.StringUtils.getFilenameExtension(file.getPath());
    }

    /**
     * <dt>Description:getFilename</dt>
     * <dd>从文件路径中取出文件名
     * <p>windows 可以建点开头(.aaa)和含有点(a.aa)的目录 windows 可以建点开头(.aaa)的文件</p></dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 16:51</i></dd>
     *
     * @param path 文件路径
     * @return java.lang.String
     * @see org.springframework.util.StringUtils#cleanPath
     * @since jdk1.7+
     */
    public static String getFilename(String path) {
        String r = org.springframework.util.StringUtils.getFilename(org.springframework.util.StringUtils.cleanPath(path));
        File f = new File(path);
        if (f.exists() && f.isFile()) {
            String filenameExtension = getFilenameExtension(f);
            r = filenameExtension == null ? r : r.replaceAll("." + filenameExtension, "");
        }
        return r;
    }

    /**
     * <dt>Description:takeOutFileDir</dt>
     * <dd>从文件路径中取出目录</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 17:15</i></dd>
     *
     * @param path 文件路径
     * @return java.lang.String
     * @see org.springframework.util.StringUtils#cleanPath
     * @since jdk1.7+
     */
    public static String takeOutFileDir(String path) {
        path = org.springframework.util.StringUtils.cleanPath(path);
        int pos = path.lastIndexOf("/");
        if (pos != -1) {
            return path.substring(0, pos + 1);
        } else {
            return "";
        }
    }

    /**
     * <dt>Description:newExistFileName</dt>
     * <dd>文件名已存在修改<p>
     * 原有文件名加后缀</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 17:36</i></dd>
     *
     * @param existFile 已存在的文件
     * @return java.io.File
     * @since jdk1.7+
     */
    public static File newExistFileName(File existFile) {
        return newExistFileName(existFile, null);
    }

    /**
     * <dt>Description:newExistFileName</dt>
     * <dd>文件名已存在修改<p>
     * 原有文件名加后缀</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 17:36</i></dd>
     *
     * @param existFile 已存在的文件
     * @param append    附加名称
     * @return java.io.File
     * @since jdk1.7+
     */
    public static File newExistFileName(File existFile, String append) {
        String filenameExtension = FileUtils.getFilenameExtension(existFile);
        String name = existFile.getName();
        StringBuffer newFileName = new StringBuffer();
        if (filenameExtension != null) {
            newFileName.append(name.substring(0, name.length() - filenameExtension.length() - 1));
        } else {
            newFileName.append(name);
        }
        newFileName.append("_");

        if (append != null) {
            newFileName.append(append);
        } else {
            newFileName.append(StringUtils.getRandomString(6));
        }

        if (filenameExtension != null) {
            newFileName.append(".").append(filenameExtension);
        }
        return new File(existFile.getParent(), newFileName.toString());
    }

//    /**
//     * 删除目录下所有空目录
//     * <p>
//     *
//     * @param dir
//     * @author WangNaiFei, WangNaiFei@senyint.com
//     * @date 2017年1月23日 下午1:25:50
//     * @version 1.0
//     */
//    public static void deleteEmptyFolder(File dir) {
//        if (dir != null && dir.exists() && dir.isDirectory()) {
//            File[] listFiles = dir.listFiles();
//            for (File f : listFiles) {
//                if (f.isDirectory()) {
//                    deleteEmptyFolder(f);
//                    f.delete();
//                } else {
//                    if ("Thumbs.db".equalsIgnoreCase(f.getName())) {
//                        f.delete();
//                    }
//                }
//            }
//        }
//    }

    /**
     * <dt>Description:removeFileBOM</dt>
     * <dd>去掉文本文件用windows自带文本编辑器修改后头里面的BOM,groovy文件读取用</dd>
     * <dd>author: wnfbx@163.com</dd>
     * <dd>version:1.0 <i>2022/1/15 18:54</i></dd>
     *
     * @param oldFile 旧文件
     * @since jdk1.7+
     */
    public static void removeFileBOM(File oldFile) {
        if (!oldFile.exists()) {
            return;
        }
        if (oldFile.length() <= 3) {
            return;
        }
        try (RandomAccessFile raf = new RandomAccessFile(oldFile, "r");) {
            byte readByte = (byte) raf.read();
            if (readByte != (byte) 0xef) {
                return; // not include BOM
            }
        } catch (Exception e) {
            log.error("checkFileBOM error", e);
        }

        File newFile = new File(oldFile.getPath() + ".new");
        try (FileOutputStream fileOutputStream = new FileOutputStream(newFile); FileInputStream fis = new FileInputStream(oldFile)) {
            for (int i = 0; i < 3; ++i) {// 跳过BOM
                fis.read();
            }
            IOUtil.copy(fis, fileOutputStream, new byte[1024 * 64]);
        } catch (Exception e) {
            log.error("removeFileBOM error", e);
        }

        if (oldFile.delete()) {
            newFile.renameTo(oldFile);
        }

    }

}